﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Text;
using System.Diagnostics;
using System.Net.Sockets;
using System.Net;
using System.Text.RegularExpressions;


namespace XiaoneiAPI {
    public class ApiClient {
        private readonly static string XIAONEI_API_LOGIN = "http://login.xiaonei.com/L.do?api_key=@api_key&v=1.0";
        private readonly static string XIAONEI_API_SERVER_URL =
            "http://api.xiaonei.com/restserver.do?v=1.0";
        private readonly static string XIAONEI_API_GET_SESSION_KEY =
            "http://api.xiaonei.com/restserver.do?method=xiaonei.auth.getSession&v=1.0&api_key=@api_key&auth_token=@auth_token";

        private string _apiKey;
        private string _authKey;
        private string _sessionKey;
        private int _callbackPort;
        private WebClient _client;

        public String SessionKey {
            get { return _sessionKey; }
        }

        public ApiClient(string apiKey, int callbackPort) {
            _apiKey = apiKey;
            _callbackPort = callbackPort;
            _client = new WebClient();
        }

        public bool InitClient() {
            if (SessionKey == String.Empty) {
                throw new InvalidOperationException("只能调用调用InitClient()一次");
            }
            else {
                string loginUrl = XIAONEI_API_LOGIN.Replace("@api_key", _apiKey);

                Process.Start(loginUrl);

                TcpListener callbackListener = new TcpListener(
                    new System.Net.IPAddress(new byte[] { 127, 0, 0, 1 }),
                    _callbackPort
                );

                callbackListener.Start();
                Socket inSocket = callbackListener.AcceptSocket();

                byte[] buffer = new byte[1024];
                inSocket.Receive(buffer);
                inSocket.Close();

                _authKey = GetAuthKey(Encoding.UTF8.GetString(buffer));
                _sessionKey = GetSessionKey();

                return true;
            }
        }

        /// <summary>
        /// 调用指定的API
        /// </summary>
        /// <param name="method">API方法名，应为xiaonei.XXXX.XXX，不是方法标题，比如xiaonei.friends.getFriends,不是Friends.GetFriends</param>
        /// <param name="paras">调用API的参数，具体参加API文档，不要包括api_key,v,session_key,method</param>
        /// <returns>返回的XML文档</returns>
        public XmlDocument InvokApi(string method, IDictionary<string, string> paras) {
            if (SessionKey == String.Empty) {
                throw new InvalidOperationException("必须首先调用InitClient()，初始化SessionKey");
            }
            else {
                string invokMethodUrl = CreateInvokeUrl(method, paras);
                byte[] response = _client.DownloadData(invokMethodUrl);
                string responseString = Encoding.UTF8.GetString(response);

                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(responseString);
                return xmlDoc;
            }
        }

        private string GetAuthKey(string callbackRevice) {
            Match match = Regex.Match(callbackRevice, "auth_token=(?<authKey>.*?) ");
            return match.Groups["authKey"].Value;
        }

        private string GetSessionKey() {

            string requestSessionKey = XIAONEI_API_GET_SESSION_KEY.Replace("@api_key", _apiKey).Replace("@auth_token", _authKey);
            string sessionResponse = Encoding.ASCII.GetString(_client.DownloadData(requestSessionKey));

            Match sessionKeyMath = Regex.Match(sessionResponse, "<session_key>(?<sessionKey>.*?)</session_key>");

            return sessionKeyMath.Groups["sessionKey"].Value;
        }

        private string CreateInvokeUrl(string method, IDictionary<string, string> paras) {
            string url = XIAONEI_API_SERVER_URL + "&api_key=" + _apiKey
                + "&session_key=" + _sessionKey
                + "&method=" + method;

            foreach (KeyValuePair<string, string> para in paras) {
                url += "&" + para.Key + "=" + para.Value;
            }

            return url;
        }
    }
}
